# PyGui (PieGuy)

import pygame
from pygame import *

import os
import glob
import pygame; from pygame.locals import *

class watched:
	
	def watch(self, watcher, watched):
		if "watchers" in self.__dict__:
			if watched in self.watchers:
				self.watchers[watched].append(watcher)
			else:
				self.watchers[watched] = [watcher]
		else:
			self.__dict__["watchers"] = {}
			self.watchers[watched] = [watcher]
		
	def __setattr__(self, key, value):
		self.__dict__[key] = value
		if "watchers" in self.__dict__ and key in self.watchers:
			for w in self.watchers[key]:
				w.inform(self, key, value)
	
	def __delattr__(self, key):
		del self.__dict__[key]
		if "watchers" in self.__dict__ and key in self.watchers:
			for w in self.watchers[key]:
				w.removed(self, key, value)
		
class window:
	def __init__(self, name, rect=None, z=0):
		if rect is None:
			rect = pygame.Rect(0,0,0,0)
		self.rect = rect
		self.name = name
		self.z = z
		self.childrens = {}
		self.childrenz = []
		self.parent = None
		self.visible = 1
		self.alignment = (0,0)
		self.alignto = None
		
	
	def __del__(self):
		self.clear()
		
	#allows us to sort a list of windows by depth
	def __cmp__(self, other):
		return cmp(other.z, self.z)
		
	#because I can and someone may need it.
	def __len__(self):
		return len(childrens)
		
	#allow children to be called up easier.
	def __getitem__(self, key):
		return self.childrens[key]
		
	def add(self, window):
		if window.name in self.childrens:
			return 0
		
		window.parent = self
		self.childrens[window.name] = window
		self.updz()
		return 1
		
	def remove(self, name):
		try:
			del self.childrens[name]	
		except:
			
			pass #fail silently.
		self.updz()
		
	def clear(self):
		for item in self.childrens:
			del item
		self.childrens = {}
			
	def lineage(self):
		if self.parent is None:
			return [self]
		else:
			list = self.parent.lineage()
			list.append(self)
			return list
			
	def updz(self):
		self.childrenz = []
		for name, kid in self.childrens.iteritems():
			self.childrenz.append(kid)
		self.childrenz.sort()
		
	def render(self, screen, offs = (0, 0)):
		return
		
	def draw(self, screen, offs = (0, 0)):
		if self.visible is 0:
			return
		offsx = self.rect[0] + offs[0]
		offsy = self.rect[1] + offs[1]
		offs = offsx, offsy
		self.render(screen, offs)
		for kid in self.childrenz:
			kid.draw(screen, offs)
		
	#you will find this especially useful for fitting buttons over images
	def fit(self, other = None):
		if other is None:
			other = self.parent
		self.rect = pygame.Rect((0,0) , other.rect.size)
		
	#like fit, but only resizes, doesnt move.
	def stretch(self, other = None):
		if other is None:
			other = self.parent
		if self.rect is None:
			self.rect = pygame.Rect((0,0),other.rect.size)
		else:
			self.rect.size = other.rect.size
			
	#keep aligned
	#dir: [0] is horizontal, [1] is vertical
	#0: no align 1: left/top 2: center 3: right/bottom 4: internal left/top 5: internal center 6: internal right/bottom
	#If other is none, align to parent.
	def align(self, dir = (2,2), other = None):
		if other is None:
			other = self.parent
		
		if dir[0] == 4:
			self.originx = self.rect.left
		elif dir[0] == 5:
			self.originx = self.rect.centerx
		elif dir[0] == 6:
			self.originx = self.rect.right
		
		if dir[1] == 4:
			self.originy = self.rect.top
		elif dir[1] == 5:
			self.originy = self.rect.centery
		elif dir[1] == 6:
			self.originy = self.rect.bottom
			
		self.alignto = other
		self.alignment = dir
		self.realign()
		
	#re-align according to align rule.
	def realign(self):
		if self.alignto is None:
			return
		if self.alignment == (0,0):
			return
			
		if self.alignment[0] == 1:
			self.rect.left = 0
		elif self.alignment[0] == 2:
			self.rect.centerx = self.alignto.rect.width / 2
		elif self.alignment[0] == 3:
			self.rect.right = self.alignto.rect.width
		elif self.alignment[0] == 4:
			self.rect.left = self.originx
		elif self.alignment[0] == 5:
			self.rect.centerx = self.originx
		elif self.alignment[0] == 6:
			self.rect.right = self.originx
			
		if self.alignment[1] == 1:
			self.rect.top = 170
		elif self.alignment[1] == 2:
			self.rect.centery = self.alignto.rect.height / 2
		elif self.alignment[1] == 3:
			self.rect.bottom = self.alignto.rect.height
		elif self.alignment[1] == 4:
			self.rect.top = self.originy
		elif self.alignment[1] == 5:
			self.rect.centery = self.originy
		elif self.alignment[1] == 6:
			self.rect.bottom = self.originy

	def down(self, pos, button):
		return 0;
	
	def up(self, pos, button):
		return 0;
	
	def move(self, pos, rel, buttons):
		return 0;
		
	def mousedown(self, pos, button):	
		if self.visible is 0:
			return
		posx = pos[0] - self.rect[0]
		posy = pos[1] - self.rect[1]
		pos2 = posx, posy
			
		for kid in self.childrenz:
			kid.mousedown(pos2, button)
			
		self.down(pos, button)	
		
	def mouseup(self, pos, button):	
		if self.visible is 0:
			return		
		posx = pos[0] - self.rect[0]
		posy = pos[1] - self.rect[1]
		pos2 = posx, posy

		for kid in self.childrenz:
			kid.mouseup(pos2, button)
			
		self.up(pos, button)
	
	def mousemove(self, pos, rel, buttons):
		if self.visible is 0:
			return
		posx = pos[0] - self.rect[0]
		posy = pos[1] - self.rect[1]
		pos2 = posx, posy
			
		for kid in self.childrenz:
			kid.mousemove(pos2, rel, buttons)
			
		self.move(pos, rel, buttons)
			
class wblock(window):
	def __init__(self, name, rect=None, color=(255,0,255), alpha=255, z=0):
		window.__init__(self, name, rect, z)
		self.color = color
		self.alpha = alpha
		
	def render(self, screen, offs = (0, 0)):
		self.surf = pygame.Surface(self.rect.size).convert()
		self.surf.fill(self.color)
		self.surf.set_alpha(self.alpha)
		screen.blit(self.surf, offs)
			
class wtext(window):
	def __init__(self, name, text, rect=None, font=None, color=(255,255,255), alpha=255, z=0):
		window.__init__(self, name, rect, z)
		if font is None:
			font = pygame.font.Font(None, 24)
		self.font = font
		self.color = color
		self.alpha = alpha
		self.setText(text)
	
	def setText(self, text):
		self.text = str(text)
		self.surf = self.font.render(self.text, 1, self.color).convert_alpha()
		self.rect.size = self.surf.get_size()
		self.surf.set_alpha(self.alpha)
		self.realign()
		
	def render(self, screen, offs = (0, 0)):
		screen.blit(self.surf, offs)
		
class wimage(window):
	def __init__(self, name, surf, rect=None, z=0):
		window.__init__(self, name, rect, z)
		self.setImage(surf)
		
	def setImage(self, image):
		if type(image) == type('') or type(image) == type(u''):
			self.surf = pygame.image.load(image).convert_alpha()
		else:
			self.surf = image
		
		self.rect.size = self.surf.get_size()
		self.realign()

		
	def render(self, screen, offs = (0, 0)):
		screen.blit(self.surf, offs)
		
# function must accept a window object (self), pos (x, y) and button (as per pygame)
class wbutton(window):
	def __init__(self, name, function=None, function2=None, rect=None, z=0):
		window.__init__(self, name, rect, z)
		self.function = function
		self.function2 = function2
	
	def down(self, pos, button):	
		if self.function is not None:
			if self.rect.collidepoint(pos[0], pos[1]) == 1:
				self.function(self, pos, button)
				
	def up(self, pos, button):
		if self.function2 is not None:
			self.function2(self, pos, button)
		
#generic usage: add as the child to a window, set the size to the draggable area.
#To allow the entire window to be dragged, use fit()
class wdragger(window):
	def __init__(self, name, rect=None, other = None, z=0):
		window.__init__(self, name, rect, z)
		self.other = other
		self.dragged = 0
		
	def down(self, pos, button):
		if self.rect.collidepoint(pos[0], pos[1]) == 1:
			self.dragged = 1
		
	def up(self, pos, button):
		self.dragged = 0
		
	def move(self, pos, rel, buttons):
		if self.dragged is 1:
			if self.other is None:
				self.parent.rect.move_ip(rel)
			else:
				self.other.rect.move_ip(rel)

class wvartext(wtext):
	def __init__(self, name, obj, attr, prefix="", rect=None, font=None, color=(255,255,255), alpha=255, z=0):
		wtext.__init__(self, name, getattr(obj, attr), rect, font, color, alpha, z)
		self.obj = obj
		self.attr = attr
		self.prefix = prefix
		obj.watch(self, attr)
		
	def inform(self, obj, attr, value):
		if self.prefix is not None:
			self.setText(self.prefix + str(value))
		else:
			self.setText(str(value))
		
class windowmanager:
	
	root = None
	
	def __getitem__(self, key):
		return self.root[key]
	
	def setup(self, screen):
		self.screen = screen
		self.root = window("root", pygame.Rect(screen.get_rect()))

	def draw(self):
		if self.root is None:
			raise Exception, "You must call setup(screen) before draw()"
			
		self.root.draw(self.screen)
			
	def add(self, window):
		if self.root is None:
			raise Exception, "You must call setup(screen) before add()"
		self.root.add(window)
	
	def remove(self, name):
		self.root.remove(name)
	
	def injectEvent(self, event):
		if event.type == MOUSEBUTTONDOWN:
			self.root.mousedown(event.pos, event.button)
		elif event.type == MOUSEBUTTONUP:
			self.root.mouseup(event.pos, event.button)
		elif event.type == MOUSEMOTION:
			self.root.mousemove(event.pos, event.rel, event.buttons)
			
			
class TextBox(object):
    def __init__(self, message, tabcomp = True, size = 28, width = 256):
        if message == None: message = ""

        font = pygame.font.Font(None, size)
        surf = pygame.Surface([width + 2, 2 * font.get_linesize() + 4])
        font.set_bold(True)
        question = font.render(message, True, GRAY)
        surf.fill(BLACK)
        pygame.draw.rect(surf, WHITE, surf.get_rect(), 1)
        surf.blit(question, [1, 1])
        font.set_bold(False)

        self._tabcomp = tabcomp
        self._font = font
        self._surface = surf

    def ask(self, screen, string = None):
        if string == None: string = ""
        idx = len(string)
        cut = ""

        r_q = self._surface.get_rect()
        r_q.center = screen.get_rect().center
        while True:
            x_off = 10
            screen.blit(self._surface, r_q)
            for i in range(len(string)):
                c = string[i]
                if i == idx: self._font.set_underline(True)                    
                img_a = self._font.render(c, True, WHITE)
                self._font.set_underline(False)
                r_a = img_a.get_rect()
                r_a.topleft = [r_q.x + x_off,
                               r_q.y + self._font.get_linesize() + 1]
                x_off += img_a.get_width()            
                screen.blit(img_a, r_a)

            if idx == len(string):
                img_a = self._font.render("_", True, WHITE)
                r_a = img_a.get_rect()
                r_a.topleft = [r_q.x + x_off,
                               r_q.y + self._font.get_linesize() + 1]
                screen.blit(img_a, r_a)

            pygame.display.update()

            ev = pygame.event.wait()
            while ev.type != KEYDOWN:
                ev = pygame.event.wait()

            if ev.key == K_ESCAPE: return None
            elif ev.key == K_F11: pygame.display.toggle_fullscreen()
            elif ev.key == K_BACKSPACE:
                string = string[:idx - 1] + string[idx:]
                idx -= 1
            elif ev.key == K_DELETE:
                string = string[:idx] + string[idx + 1:]
            elif ev.key == K_RETURN: break
            elif ev.key == K_END: idx = len(string)
            elif ev.key == K_HOME: idx = 0
            elif ev.key == K_LEFT: idx = max(0, idx - 1)
            elif ev.key == K_RIGHT: idx = min(len(string), idx + 1)
            elif ev.key == K_TAB:
                if self._tabcomp: string = self._complete(string)
                idx = len(string)
            elif ev.key < 128:
                if ev.mod & KMOD_CTRL:
                    if len(string) == 0: pass
                    elif ev.key == K_u:
                        string = ""
                        idx = 0
                    elif ev.key == K_h: string = string[0:-1]
                    elif ev.key == K_e: idx = len(string)
                    elif ev.key == K_a: idx = 0
                    elif ev.key == K_f: idx = min(len(string), idx + 1)
                    elif ev.key == K_b: idx = max(0, idx - 1)
                    elif ev.key == K_t:
                        if idx == 0: idx = 1
                        if len(string) == 1: pass
                        elif idx == len(string):
                            string = string[:-2] + string[-1] + string[-2]
                        else:
                            string = (string[:idx - 1] + string[idx] +
                                      string[idx - 1] + string[idx + 1:])
                            idx = min(len(string), idx + 1)
                    elif ev.key == K_k:
                        cut = string[idx:]
                        string = string[:idx]
                    elif ev.key == K_y:
                        string = string[:idx] + cut + string[idx:]
                        idx += len(cut)
                    elif ev.key == K_w:
                        if string[-1] == "/": string = string[:-1]
                        string = string[:string.rfind("/") + 1]
                else:
                    string = string[:idx] + ev.unicode + string[idx:]
                    idx += 1

        return string

    def _complete(self, string):
        if '*' not in string: string += "*"

        g = glob.glob(string)
        if len(g) == 0: s = string[:-1]
        elif len(g) == 1: s = g[0]
        else: # thanks, mu
            for i, chars in enumerate(zip(*g)):
                if min(chars) != max(chars):
                    j = i
                    break
            else: j = min([len(s) for s in g])
            s = g[0][:j]
        s = os.path.expanduser(s)
        if os.path.isdir(s) and s[-1] != "/": s += "/"
        return s
		
wmgr = windowmanager()

BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
GRAY = [128, 128, 128]
ALIGN_NONE = (0,0)
ALIGN_TOP = (0,1)
ALIGN_MIDDLE = (0,2)
ALIGN_BOTTOM = (0,3)
ALIGN_LEFT = (1,0)
ALIGN_CENTER = (2,0)
ALIGN_RIGHT = (3,0)
ALIGN_TOPLEFT = (1,1)
ALIGN_TOPCENTER = (2,1)
ALIGN_TOPRIGHT = (3,1)
ALIGN_MIDLEFT = (1,2)
ALIGN_MIDCENTER = (2,2)
ALIGN_MIDRIGHT = (3,2)
ALIGN_BOTTOMLEFT = (1,3)
ALIGN_BOTTOMCENTER = (2,3)
ALIGN_BOTTOMRIGHT = (3,3)

ALIGN_MYLEFT = (4, 0)
ALIGN_MYCENTER = (5, 0)
ALIGN_MYRIGHT = (6, 0)
